Кратко пробежаться по плану, подчеркнуть, что сегодня центральная тема — процессы и потоки, а виртуализация и контейнеризация будут рассмотрены как практическое применение этих концепций. На всю лекцию заложено ~90 минут.
Этот слайд — введение. Спросить студентов: «Какие ресурсы ОС вы уже использовали в программировании?» Упомянуть, что дальше мы подробно разберём каждый пункт. На слайде 2-3 минуты.
Ключевое различие: программа — это файл на диске, процесс — это выполняющийся экземпляр. Показать `ps aux` в терминале для наглядности. Спросить: «Чем отличается процесс от потока?» — ответ дадим позже.
Нарисовать переходы на доске и проговорить каждый. Типичный вопрос: «Что заставляет процесс перейти из Выполнения в Ожидание?» — системный вызов I/O, ожидание ресурса. Обратить внимание, что из Ожидания процесс переходит в Готов, а не напрямую в Выполнение.
Подчеркнуть, что Windows-подход — явное создание нового процесса с загрузкой исполняемого файла. Обратить внимание на структуры STARTUPINFO и PROCESS_INFORMATION — они содержат много параметров, которые по умолчанию можно оставить NULL. Не застревать на каждом параметре, главное — общая картина.
Ключевой слайд. Обязательно разобрать fork() по строкам. Распространённая ошибка студентов — забывать, что после fork() код выполняется в двух процессах. Спросить: «Что выведет printf, если поставить его до if?» Показать copy-on-write на примере: пока процессы не модифицируют память, страницы не разделяются.
Виртуальная память уже знакома студентам из архитектуры ЭВМ — связать с этим. Основной акцент на том, что виртуализация решает проблему изоляции и эффективного использования ресурсов. Переход к следующему слайду: покажем, как виртуальная память выглядит внутри процесса.
Спросить: «Почему стек растёт вниз, а куча вверх?» — чтобы максимально использовать адресное пространство. Упомянуть утилиту `size` в Linux, которая показывает размеры секций. Обратить внимание на зарезервированную область — доступна только ядру, при обращении из userspace будет segmentation fault.
Это большой слайд — на него 10-12 минут. Namespaces изолируют «видимость» ресурсов, cgroups — ограничивают «потребление». Показать `unshare -p -f --mount-proc bash` в терминале — наглядная демонстрация. По сравнению VM vs контейнеры: контейнеры НЕ обеспечивают полную изоляцию (общее ядро!), это важно подчеркнуть для безопасности.
Подчеркнуть: Podman — drop-in замена Docker для большинства команд. Rootless-режим повышает безопасность: уязвимость в контейнере не даёт прав root на хосте. Pod — удобная абстракция для миграции в Kubernetes.
containerd — «движок под капотом». Docker с версии 1.11 использует containerd. Kubernetes может работать напрямую с containerd, минуя Docker (так называемый cri-containerd). Упомянуть, что runC — это OCI-совместимый runtime.
LXC — «классическая» контейнеризация Linux, ближе к лёгким ВМ. Docker и Podman — ориентированы на приложения. containerd — для интеграции в оркестраторы. Спросить: «Какой инструмент выбрать для локальной разработки?» — Docker или Podman.
Связать с предыдущим разделом: контейнеры используют namespaces + cgroups, но это НЕ полная изоляция. Далее рассмотрим дополнительные механизмы безопасности.
chroot — исторически первый механизм изоляции (с 1979 года). Показать, что chroot недостаточен для безопасности: продемонстрировать, что `ls /proc` внутри chroot видит процессы хоста.
USER namespace — ключевой для rootless-контейнеров в Podman. Процесс считает себя root внутри namespace, но на хосте — обычный пользователь. Спросить: «Какой namespace наиболее важен для безопасности?» — все вместе, но USER критичен для rootless.
seccomp — один из самых мощных механизмов. Docker по умолчанию запрещает ~44 системных вызова. Спросить: «Что произойдёт, если процесс вызовет запрещённый syscall?» — получит errno. Практическое задание: посмотреть seccomp-профиль Docker через `docker info`.
В контексте контейнеров: Docker поддерживает оба механизма. Podman — преимущественно SELinux (Red Hat). Спросить: «Какой механизм используется на ваших рабочих станциях?» — зависит от дистрибутива.
Job Objects — уникальный механизм Windows, не имеющий прямого аналога в Linux (cgroups частично компенсируют). В контексте безопасности: Job Object может запретить созданным внутри него процессам определённые действия (например, создание окон, доступ к буферу обмена).
Пояснить: ни один механизм не даёт полной защиты. Только комбинация нескольких подходов обеспечивает надёжную изоляцию. Именно так работают контейнеры: namespaces + cgroups + seccomp + MAC. Ссылки: man7.org — namespaces(7), capabilities(7), seccomp(2).
Показать на примере `ps -eo pid,ppid,pgid,sid,cmd`. Спросить: «Что произойдёт, если завершить лидер группы процессов?» — группа продолжает работать, но теряет лидера. Упомянуть `nohup` и `disown` как практические инструменты, которые студенты уже могли использовать.
Ключевой момент: SIGKILL и SIGSTOP невозможно перехватить или обработать — это важно для понимания управления процессами. Спросить: «Чем отличается SIGTERM от SIGKILL?» — SIGTERM позволяет процессу корректно завершиться (освободить ресурсы), SIGKILL — мгновенное убийство. Показать `kill -l` для полного списка.
Вернуться к вопросу «Чем отличается процесс от потока?» из начала лекции. Нарисовать на доске: один процесс = одна таблица страниц, много потоков = одно адресное пространство, разные стеки. Ключевая выгода: переключение между потоками дешевле, чем между процессами (не нужно переключать адресное пространство).
Акцентировать модель 1:1 — она стандартна в современных ОС. Many-to-One устарела, потому что блокировка одного потока блокирует все. Many-to-Many сложна в реализации. Упомянуть goroutines в Go как пример M:N, реализованный на уровне runtime (не ядра).
Показать параллель с CreateProcess — API Windows следует единому стилю. Обратить внимание на CloseHandle — дескриптор потока нужно закрывать, иначе утечка ресурсов. WaitForSingleObject — аналог join в POSIX.
Сравнить с Windows-версией: pthread_create проще, но меньше контроля. Важно: при компиляции нужно `-lpthread`. Спросить: «Что будет, если не вызвать pthread_join?» — процесс завершится, даже если поток ещё работает (в отличие от wait для процессов).
Обязательно разграничить конкурентность и параллелизм — это частый вопрос на экзамене. Конкурентность — про структуру программы, параллелизм — про исполнение. Показать пример: одноядерный процессор с конкурентными задачами (псевдопараллелизм) vs многоядерный с истинным параллелизмом.
Разобрать пример по шагам на доске, показав interleaving. Спросить: «Как исправить?» —.mutex/критическая секция. Кратко упомянуть deadlock: условия Коэна (взаимное исключение, удержание, отсутствие предобработки, циклическое ожидание). Проблема ABA — связь со следующей темой (атомарные операции).
Начать с вопроса: «Что произойдёт, если поток заблокируется на I/O?» — простой ответ: он не может делать ничего другого. В одно поточной программе это означает зависание интерфейса. Показать связь с отзывчивостью GUI-приложений.
IOCP в Windows — золотой стандарт для высоконагруженных серверов. В Linux эволюция: select → poll → epoll → io_uring. io_uring — самый современный механизм, основан на кольцевых буферах, минимизирует системные вызовы. Упомянуть, что epoll используется в Nginx, Redis и других высокопроизводительных системах.
SSH студенты уже знают из практики — связать с этим. Основной акцент на RPC и WMI как программных механизмах. Спросить: «Какие ещё способы удалённого запуска вы знаете?» — возможные ответы: RDP, Ansible, systemd-nspawn.
Подчеркнуть: RPC создаёт иллюзию локального вызова, но под капотом — сериализация, сетевая передача и десериализация. Спросить: «В чём недостатки RPC по сравнению с локальным вызовом?» — задержка сети, отсутствие общих ссылок, проблемы с типами данных. Упомянуть gRPC и protobuf как современную реализацию.
Пробежаться по пунктам, спросить студентов, какой раздел показался наиболее сложным. Упомянуть, что на следующей лекции углубимся в синхронизацию и примитивы взаимодействия процессов.
Рекомендовать студентам ответить на вопросы дома перед практическим занятием. Вопросы 7 и 8 — наиболее сложные, по ним будет больше задач на практике.
Задания 1 и 3 — обязательные для всех. Задание 2 полезно для понимания внутренней структуры процесса через procfs. Задание 4 — для продвинутых студентов. Напомнить: для сдачи нужен отчёт с выводами и скриншотами.